home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / a_utils / _archvrs / unix / unzip51 / zipinfo.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-11-04  |  50.6 KB  |  1,332 lines

  1. /*---------------------------------------------------------------------------
  2.  
  3.   zipinfo.c
  4.  
  5.   This program reads great gobs of totally nifty information, including the
  6.   central directory stuff, from ZIP archives ("zipfiles" for short).  It
  7.   started as just a testbed for fooling with zipfiles, but at this point it
  8.   is actually a useful utility.  It also became the basis for the rewrite of
  9.   UnZip (3.16 -> 4.0), using the central directory for processing rather than
  10.   the individual (local) file headers.
  11.  
  12.   The author finds it convenient to define an alias "ii" (under Unix and VMS)
  13.   or to rename the executable to "ii.exe" (OS/2 and DOS).  This nicely comple-
  14.   ments the common Unix "ll" long-listing alias (ls -lF), since zipinfo's de-
  15.   fault action is to produce a Unix-like listing of the archive's contents.
  16.   "ii zipfile" is easier to type than "zipinfo zipfile"...
  17.  
  18.   Another dandy product from your buddies at Newtware!
  19.  
  20.   ---------------------------------------------------------------------------
  21.  
  22.   To compile:  ZipInfo is now part of the regular UnZip compilation for the
  23.      systems listed below; see the top of unzip.c for partial instructions.
  24.  
  25.   ---------------------------------------------------------------------------
  26.  
  27.   Source:     unzip51.zip (.tar.Z, etc.) for Unix, VMS, OS/2, MS-DOS and
  28.               TOPS-20; see `Where' in source distribution for ftp, uucp and
  29.               mail-server sites.
  30.   Author:     Greg Roelofs, roe2@midway.uchicago.edu, 23 August 1990
  31.   Copyright:  Portions copyright 1992 Greg Roelofs.  Portions adapted from
  32.               unzip 3.1.  SizeOfEAs() by Kai Uwe Rommel.
  33.  
  34.   ---------------------------------------------------------------------------*/
  35.  
  36.  
  37.  
  38.  
  39. #ifndef ZIPINFO
  40. #  define ZIPINFO
  41. #endif
  42. #include "unzip.h"        /* ZIPINFO ==> Unix perms in non-Unix environments */
  43.  
  44. #define VERSION  "v1.04 of 22 Oct 92"  /* BETA version */
  45.  
  46. #define LFLAG    3        /* for short "ls -l" type listing */
  47.  
  48. #define EAID     0x0009   /* OS/2 extended-attributes extra field ID, for */
  49. typedef struct {          /*  OS/2 info in OS/2 and non-OS/2 environments */
  50.     unsigned short nID;
  51.     unsigned short nSize;
  52.     ulg lSize;
  53. } EAHEADER, *PEAHEADER;
  54.  
  55.  
  56.  
  57.  
  58. /**********************/
  59. /*  Global Variables  */
  60. /**********************/
  61.  
  62. #ifdef EBCDIC
  63.    int  aflag=1;        /* this is so you can read it on the screen  */
  64. #else                   /* (basically, entire program is "unzip -c") */
  65.    int  aflag=0;
  66. #endif
  67. int lflag=(-1);         /* '-12slmv':  listing format */
  68. int hflag=0;            /* '-h':  header line */
  69. int tflag=0;            /* '-t':  totals line */
  70. int zflag=0;            /* '-z':  zipfile comment */
  71.  
  72. uch *inbuf, *inptr;     /* input buffer (any size is legal) and pointer */
  73. int incnt;
  74.  
  75. char zipfn[FILNAMSIZ];
  76. int zipfd;              /* zipfile file handle */
  77. longint ziplen;
  78.  
  79. uch *hold;
  80. char local_hdr_sig[5] = "\120";    /* remaining signature bytes come later:  */
  81. char central_hdr_sig[5] = "\120";  /*  must initialize at runtime so zipinfo */
  82. char end_central_sig[5] = "\120";  /*  executable won't look like a zipfile  */
  83. /* char extd_local_sig[5] = "\120"; */
  84.  
  85. cdir_file_hdr crec;     /* used in zipinfo.c, misc.c */
  86. local_file_hdr lrec;
  87. ecdir_rec ecrec;
  88. struct stat statbuf;    /* used by main() */
  89.  
  90. int filespecs;          /* number of real file specifications to be matched */
  91. int xfilespecs;         /* number of excluded filespecs to be matched */
  92. int process_all_files;
  93. longint real_ecrec_offset, expect_ecrec_offset;
  94. longint extra_bytes=0;          /* used in zipinfo.c, misc.c */
  95. longint cur_zipfile_bufstart;   /* find_end_central_dir, readbuf */
  96.  
  97. min_info info, *pInfo=(&info);
  98.  
  99. uch *extra_field = NULL;        /* used by VMS, Mac and OS/2 versions */
  100. uch *outbuf;                    /* buffer for rle look-back, zipfile comment */
  101. uch *outout;                    /* scratch pad for ASCII-native trans */
  102.  
  103. char filename[FILNAMSIZ];
  104. char sig[5];
  105. static char *fnames[2] = {"*", NULL};  /* default filenames vector */
  106. char **pfnames = fnames, **pxnames = &fnames[1];
  107.  
  108. static ush hostnum;
  109. static ush methnum;
  110. static ush extnum;
  111.  
  112. char *EndSigMsg = "\nwarning:\
  113.   didn't find end-of-central-dir signature at end of central dir.\n";
  114. char *CentSigMsg =
  115.   "error:  expected central file header signature not found (file #%u).\n";
  116. char *SeekMsg =
  117.   "error:  attempt to seek before beginning of zipfile\n%s";
  118.  
  119. #ifdef VMS
  120. char *ReportMsg = "\
  121.   (please check that you have transferred or created the zipfile in the\n\
  122.   appropriate BINARY mode--this includes ftp, Kermit, AND unzip'd zipfiles)\n";
  123. #else /* !VMS */
  124. char *ReportMsg = "\
  125.   (please check that you have transferred or created the zipfile in the\n\
  126.   appropriate BINARY mode and that you have compiled unzip properly)\n";
  127. #endif /* ?VMS */
  128.  
  129.  
  130.  
  131.  
  132.  
  133.  
  134. /******************/
  135. /*  Main program  */
  136. /******************/
  137.  
  138. main(argc, argv)
  139.     int    argc;
  140.     char   *argv[];
  141. {
  142.     char   *s;
  143.     int    c, error=FALSE, negative=0;
  144.     int    hflag_slmv=TRUE, hflag_2=FALSE;  /* diff options => diff defaults */
  145.     int    tflag_slm=TRUE, tflag_2v=FALSE;
  146.     int    explicit_h=FALSE, explicit_t=FALSE;
  147.  
  148.  
  149.  
  150. /*---------------------------------------------------------------------------
  151.     Everybody is now "NOTINT16," but this is a nice little piece of code, so
  152.     just comment it out for future reference. :-)
  153.   ---------------------------------------------------------------------------*/
  154.  
  155. #if 0
  156. # ifndef KNOW_IT_WORKS  /* define this to save space, if things already work */
  157. # ifndef DOS_OS2        /* already works (no RISCy OS/2's yet...) */
  158. # ifndef NOTINT16       /* whole point is to see if this NEEDS defining */
  159.     {
  160.         int error=0;
  161.         long testsig;
  162.         static char *mach_type[3] = {"big-endian", "structure-padding",
  163.                                      "big-endian and structure-padding"};
  164.  
  165.         strcpy((char *)&testsig,"012");
  166.         if (testsig != 0x00323130)
  167.             error = 1;
  168.         if (sizeof(cdir_file_hdr) != CREC_SIZE)
  169.             error += 2;
  170.         if (error--)
  171.             fprintf(stderr, "It appears that your machine is %s.  If errors\n\
  172. occur, please try recompiling with \"NOTINT16\" defined (read the\n\
  173. Makefile, or try \"make zipinfo\").\n\n", mach_type[error]);
  174.     }
  175. # endif /* !NOTINT16 */
  176. # endif /* !DOS_OS2 */
  177. # endif /* !KNOW_IT_WORKS */
  178. #endif /* 0 */
  179.  
  180. /*---------------------------------------------------------------------------
  181.     Put environment-variable options into the queue, then rip through any
  182.     command-line options lurking about...
  183.   ---------------------------------------------------------------------------*/
  184.  
  185.     envargs(&argc, &argv, ENV_ZIPINFO);
  186.  
  187.     while (--argc > 0 && (*++argv)[0] == '-') {
  188.         s = argv[0] + 1;
  189.         while ((c = *s++) != 0) {    /* "!= 0":  prevent Turbo C warning */
  190.             switch (c) {
  191.                 case '-':
  192.                     ++negative;
  193.                     break;
  194.                 case '1':      /* shortest listing:  JUST filenames */
  195.                     if (negative)
  196.                         lflag = -2, negative = 0;
  197.                     else
  198.                         lflag = 1;
  199.                     break;
  200.                 case '2':      /* just filenames, plus headers if specified */
  201.                     if (negative)
  202.                         lflag = -2, negative = 0;
  203.                     else
  204.                         lflag = 2;
  205.                     break;
  206.                 case 'h':      /* header line */
  207.                     if (negative)
  208.                         hflag_2 = hflag_slmv = FALSE, negative = 0;
  209.                     else {
  210.                         hflag_2 = hflag_slmv = explicit_h = TRUE;
  211.                         if (lflag == -1)
  212.                             lflag = 0;
  213.                     }
  214.                     break;
  215.                 case 'l':      /* longer form of "ls -l" type listing */
  216.                     if (negative)
  217.                         lflag = -2, negative = 0;
  218.                     else
  219.                         lflag = 5;
  220.                     break;
  221.                 case 'm':      /* medium form of "ls -l" type listing */
  222.                     if (negative)
  223.                         lflag = -2, negative = 0;
  224.                     else
  225.                         lflag = 4;
  226.                     break;
  227.                 case 's':      /* default:  shorter "ls -l" type listing */
  228.                     if (negative)
  229.                         lflag = -2, negative = 0;
  230.                     else
  231.                         lflag = 3;
  232.                     break;
  233.                 case 't':      /* totals line */
  234.                     if (negative)
  235.                         tflag_2v = tflag_slm = FALSE, negative = 0;
  236.                     else {
  237.                         tflag_2v = tflag_slm = explicit_t = TRUE;
  238.                         if (lflag == -1)
  239.                             lflag = 0;
  240.                     }
  241.                     break;
  242.                 case 'v':      /* turbo-verbose listing */
  243.                     if (negative)
  244.                         lflag = -2, negative = 0;
  245.                     else
  246.                         lflag = 10;
  247.                     break;
  248.                 case 'z':      /* print zipfile comment */
  249.                     if (negative)
  250.                         zflag = negative = 0;
  251.                     else
  252.                         zflag = 1;
  253.                     break;
  254.                 default:
  255.                     error = TRUE;
  256.                     break;
  257.             }
  258.         }
  259.     }
  260.     if ((argc-- == 0) || error)
  261.         RETURN(usage(error));
  262.  
  263.     if (argc != 0)
  264.         process_all_files = FALSE;
  265.     else
  266.         process_all_files = TRUE;   /* for speed */
  267.  
  268.     /* if no listing options given (or all negated), or if only -h/-t given
  269.      * with individual files specified, use default listing format */
  270.     if ((lflag < 0) || (!process_all_files && (lflag == 0)))
  271.         lflag = LFLAG;
  272.  
  273.     /* set header and totals flags to default or specified values */
  274.     switch (lflag) {
  275.         case 0:   /* 0:  can only occur if either -t or -h explicitly given; */
  276.         case 2:   /*  therefore set both flags equal to normally false value */
  277.             hflag = hflag_2;
  278.             tflag = tflag_2v;
  279.             break;
  280.         case 1:   /* only filenames, *always* */
  281.             hflag = FALSE;
  282.             tflag = FALSE;
  283.             zflag = FALSE;
  284.             break;
  285.         case 3:
  286.         case 4:
  287.         case 5:
  288.             hflag = (!process_all_files && !explicit_h)? FALSE : hflag_slmv;
  289.             tflag = (!process_all_files && !explicit_t)? FALSE : tflag_slm;
  290.             break;
  291.         case 10:
  292.             hflag = hflag_slmv;
  293.             tflag = tflag_2v;
  294.             break;
  295.     }
  296.  
  297. /*---------------------------------------------------------------------------
  298.     Now get the zipfile name from the command line and see if it exists as a
  299.     regular (non-directory) file.  If not, append the ".zip" suffix.  We don't
  300.     immediately check to see if this results in a good name, but we will do so
  301.     later.  In the meantime, see if there are any member filespecs on the com-
  302.     mand line, and if so, set the filename pointer to point at them.
  303.   ---------------------------------------------------------------------------*/
  304.  
  305.     strcpy(zipfn, *argv++);
  306.     if (stat(zipfn, &statbuf) || (statbuf.st_mode & S_IFMT) == S_IFDIR)
  307.         strcat(zipfn, ZSUFX);
  308. #ifdef UNIX  /* no extension on Unix exe's--might find zip, not zip.zip; etc. */
  309.     else if (statbuf.st_mode & S_IXUSR)
  310.         fprintf(stderr, "\nnote:  file [ %s ] may be an executable\n\n", zipfn);
  311. #endif
  312.  
  313.     if (stat(zipfn, &statbuf)) {    /* try again */
  314.         fprintf(stderr, "error:  can't find zipfile [ %s ]\n", zipfn);
  315.         RETURN(PK_NOZIP);
  316.     } else
  317.         ziplen = statbuf.st_size;
  318.  
  319.     filespecs = argc;
  320.     xfilespecs = 0;
  321.  
  322.     if (!process_all_files) {
  323.         char **pp = argv-1;
  324.  
  325.         pfnames = argv;
  326.         while (*++pp)
  327.             if (!strcmp(*pp, "-x")) {
  328.                 if (pp > argv) {
  329.                     *pp = 0;           /* terminate pfnames */
  330.                     filespecs = pp - pfnames;
  331.                 } else {
  332.                     pfnames = fnames;  /* defaults */
  333.                     filespecs = 0;
  334.                 }
  335.                 pxnames = pp + 1;      /* excluded-names ptr starts after -x */
  336.                 xfilespecs = argc - filespecs - 1;
  337.                 break;                 /* skip rest of args */
  338.             }
  339.     }
  340.  
  341. /*---------------------------------------------------------------------------
  342.     Okey dokey, we have everything we need to get started.  Let's roll.
  343.   ---------------------------------------------------------------------------*/
  344.  
  345.     inbuf = (uch *)malloc(INBUFSIZ + 4);     /* 4 extra for hold[] (below) */
  346.     outbuf = (uch *)malloc(OUTBUFSIZ + 1);   /* 1 extra for string termin. */
  347.     if (aflag)                  /* if need an ascebc scratch, */
  348.         outout = (uch *)malloc(OUTBUFSIZ);
  349.     else                        /*  allocate it... */
  350.         outout = outbuf;        /*  else just point to outbuf */
  351.  
  352.     if ((inbuf == NULL) || (outbuf == NULL) || (outout == NULL)) {
  353.         fprintf(stderr, "error:  can't allocate zipinfo buffers\n");
  354.         RETURN(PK_MEM);
  355.     }
  356.     hold = &inbuf[INBUFSIZ];    /* to check for boundary-spanning signatures */
  357.  
  358.     RETURN(process_zipfile());  /* keep passing errors back... */
  359.  
  360. } /* end main() */
  361.  
  362.  
  363.  
  364.  
  365.  
  366. /**********************/
  367. /*  Function usage()  */
  368. /**********************/
  369.  
  370. int usage(error)
  371.     int error;
  372. {
  373.     FILE *usagefp;
  374.  
  375.  
  376. /*---------------------------------------------------------------------------
  377.     If user requested usage, send it to stdout; else send to stderr.
  378.   ---------------------------------------------------------------------------*/
  379.  
  380.     if (error)
  381.         usagefp = (FILE *)stderr;
  382.     else
  383.         usagefp = (FILE *)stdout;
  384.  
  385.     fprintf(usagefp, "\
  386.    ZipInfo:  Zipfile Information Utility %s\n\
  387.    (brought to you by Newtware, Inc., and the fine folks at Info-ZIP)\n\n\
  388.    Usage:  zipinfo [-12smlvhtz] file[.zip] [list...] [-x xlist...]\n",
  389.       VERSION);
  390.     fprintf(usagefp, "\
  391.      -1  list filenames ONLY, one per line (useful for pipes)\n\
  392.      -2  list filenames only, but also allow -h, -t and -z options\n\
  393.      -s  list zipfile info in short Unix \"ls -l\" format:  default\n\
  394.      -m  list zipfile info in medium Unix \"ls -l\" format\n\
  395.      -l  list zipfile info in long Unix \"ls -l\" format\n\
  396.      -v  list zipfile information in verbose, multi-page format\n\
  397.      -h  list header line\n\
  398.      -t  list totals for files listed or for all files\n\
  399.      -z  print zipfile comment\n\
  400.      -x  exclude filenames that follow from listing\n");
  401. /*
  402.      -p  disable automatic \"more\" function (for pipes) [not implemented]\n");
  403.  */
  404.  
  405. #ifdef VMS
  406.     fprintf(usagefp, "\nRemember that non-lowercase filespecs must be quoted\
  407.  in VMS (e.g., \"Makefile\").\n");
  408. #endif
  409.  
  410.     if (error)
  411.         return PK_PARAM;
  412.     else
  413.         return PK_COOL;   /* just wanted usage screen: no error */
  414.  
  415. } /* end function usage() */
  416.  
  417.  
  418.  
  419.  
  420.  
  421. /********************************/
  422. /*  Function process_zipfile()  */
  423. /********************************/
  424.  
  425. int process_zipfile()   /* return PK-type error code */
  426. {
  427.     int error=PK_COOL, error_in_archive;
  428.  
  429.  
  430. /*---------------------------------------------------------------------------
  431.     Open the zipfile for reading and in BINARY mode to prevent CR/LF trans-
  432.     lation, which would corrupt the bitstreams.
  433.   ---------------------------------------------------------------------------*/
  434.  
  435. #ifdef VMS
  436.     if (check_format())   /* check for variable-length format */
  437.         return PK_ERR;
  438. #endif /* VMS */
  439.  
  440.     if (open_input_file())      /* this should never happen, given the */
  441.         return PK_NOZIP;        /*   stat() test in main(), but... */
  442.  
  443. /*---------------------------------------------------------------------------
  444.     Reconstruct the various PK signature strings, and find and process the
  445.     end-of-central-directory header.
  446.   ---------------------------------------------------------------------------*/
  447.  
  448.     strcat(local_hdr_sig, LOCAL_HDR_SIG);
  449.     strcat(central_hdr_sig, CENTRAL_HDR_SIG);
  450.     strcat(end_central_sig, END_CENTRAL_SIG);
  451. /*  strcat(extd_local_sig, EXTD_LOCAL_SIG);  */
  452.  
  453.     /* check whole file in case a transfer protocol appended garbage */
  454.     if ((error_in_archive = find_end_central_dir(ziplen)) != 0  ||
  455.         (error_in_archive = process_end_central_dir()) > PK_WARN) {
  456.         close(zipfd);
  457.         return error_in_archive;
  458.     }
  459.  
  460. /*---------------------------------------------------------------------------
  461.     Test the end-of-central-directory info for incompatibilities (multi-disk
  462.     archives) or inconsistencies (missing or extra bytes in zipfile).
  463.   ---------------------------------------------------------------------------*/
  464.  
  465.     if (ecrec.number_this_disk != ecrec.num_disk_with_start_central_dir) {
  466.         fprintf(stderr, "\n\
  467.      Zipfile is part of a multi-disk archive, and this is not the disk on\
  468.      which the central zipfile directory begins.\n");
  469.         error_in_archive = PK_FIND;
  470.     } else {
  471.         if ((extra_bytes = real_ecrec_offset - expect_ecrec_offset) < 0) {
  472.             fprintf(stderr, "\nerror:  missing %ld bytes in zipfile (\
  473. attempting to process anyway)\n\n", -extra_bytes);
  474.             error_in_archive = PK_ERR;
  475.         } else if (extra_bytes > 0) {
  476.             if ((ecrec.offset_start_central_directory == 0) &&
  477.                 (ecrec.size_central_directory != 0))   /* zip 1.5 -go bug */
  478.             {
  479.                 fprintf(stderr, "\nerror:  NULL central directory offset (\
  480. attempting to process anyway)\n\n");
  481.                 error_in_archive = PK_ERR;
  482.             } else {
  483.                 fprintf(stderr, "\nwarning:  extra %ld bytes at beginning or\
  484.  within zipfile\n          (attempting to process anyway)\n\n", extra_bytes);
  485.                 error_in_archive = PK_WARN;
  486.             }
  487.         }
  488.  
  489.     /*-----------------------------------------------------------------------
  490.         Check for empty zipfile and exit now if so.
  491.       -----------------------------------------------------------------------*/
  492.  
  493.         if (expect_ecrec_offset == 0L  &&  ecrec.size_central_directory == 0) {
  494.             printf("%sEmpty zipfile.\n", lflag>9 ? "\n  " : "");
  495.             close(zipfd);
  496.             return (error_in_archive > PK_WARN)? error_in_archive : PK_WARN;
  497.         }
  498.  
  499.     /*-----------------------------------------------------------------------
  500.         Compensate for missing or extra bytes, and seek to where the start
  501.         of central directory should be.  If header not found, uncompensate
  502.         and try again (necessary for at least some Atari archives created
  503.         with STZIP, as well as archives created by J.H. Holm's ZIPSPLIT 1.1).
  504.       -----------------------------------------------------------------------*/
  505.  
  506.         LSEEK( ecrec.offset_start_central_directory )
  507.         if ((readbuf(sig, 4) <= 0) || strncmp(sig, central_hdr_sig, 4)) {
  508.             longint tmp = extra_bytes;
  509.  
  510.             extra_bytes = 0;
  511.             LSEEK( ecrec.offset_start_central_directory )
  512.             if ((readbuf(sig, 4) <= 0) || strncmp(sig, central_hdr_sig, 4)) {
  513.                 fprintf(stderr,
  514.             "error:  start of central directory not found; zipfile corrupt.\n");
  515.                 fprintf(stderr, ReportMsg);
  516.                 close(zipfd);
  517.                 return PK_BADERR;
  518.             }
  519.             fprintf(stderr, "error:  reported length of central directory is \
  520. %d bytes too long\n        (Atari STZIP zipfile?  J.H.Holm ZIPSPLIT 1.1 \
  521. zipfile?).\n        Compensating...\n\n", -tmp);
  522.             error_in_archive = PK_ERR;
  523.         }
  524.  
  525.     /*-----------------------------------------------------------------------
  526.         Seek to the start of the central directory one last time, since we
  527.         have just read the first entry's signature bytes; then do the central
  528.         directory and close the zipfile.
  529.       -----------------------------------------------------------------------*/
  530.  
  531.         LSEEK( ecrec.offset_start_central_directory )
  532.         if ((error = process_central_dir()) > error_in_archive)
  533.             error_in_archive = error;    /* don't overwrite stronger error */
  534.         if (lflag > 9)
  535.             printf("\n");
  536.     }
  537.  
  538.     close(zipfd);
  539.     return error_in_archive;
  540.  
  541. } /* end function process_zipfile() */
  542.  
  543.  
  544.  
  545.  
  546.  
  547. /****************************************/
  548. /*  Function process_end_central_dir()  */
  549. /****************************************/
  550.  
  551. int process_end_central_dir()   /* return PK-type error code */
  552. {
  553.     int  error = PK_COOL;
  554.  
  555.  
  556. /*--------------------------------------------------------------------------
  557.     Print out various interesting things about the zipfile.
  558.   ---------------------------------------------------------------------------*/
  559.  
  560.     /* header fits on one line, for anything up to 10GB and 10000 files: */
  561.     if (hflag)
  562.         printf((strlen(zipfn)<39)? "Archive:  %s   %ld bytes   %d file%s\n"
  563.           : "Archive:  %s   %ld   %d\n", zipfn, ziplen,
  564.           ecrec.total_entries_central_dir,
  565.           (ecrec.total_entries_central_dir==1)? "":"s");
  566.  
  567.     /* verbose format */
  568.     if (lflag > 9) {
  569.         printf("\nEnd-of-central-directory record:\n");
  570.         printf("-------------------------------\n\n");
  571.  
  572.         printf("\
  573.   Actual offset of end-of-central-dir record:   %9ld (%.8lXh)\n\
  574.   Expected offset of end-of-central-dir record: %9ld (%.8lXh)\n\
  575.   (based on the length of the central directory and its expected offset)\n\n",
  576.           real_ecrec_offset, real_ecrec_offset,
  577.           expect_ecrec_offset, expect_ecrec_offset);
  578.  
  579.         if (ecrec.number_this_disk == 0) {
  580.             printf("\
  581.   This zipfile constitutes the sole disk of a single-part archive; its\n\
  582.   central directory contains %u %s.  The central directory is %lu\n\
  583.   (%.8lXh) bytes long, and its (expected) offset in bytes from the\n\
  584.   beginning of the zipfile is %lu (%.8lXh).\n\n",
  585.               ecrec.total_entries_central_dir,
  586.               (ecrec.total_entries_central_dir == 1)? "entry" : "entries",
  587.               ecrec.size_central_directory, ecrec.size_central_directory,
  588.               ecrec.offset_start_central_directory,
  589.               ecrec.offset_start_central_directory);
  590.         } else {
  591.             printf("\
  592.   This zipfile constitutes disk %u of a multi-part archive.  The central\n\
  593.   directory starts on disk %u; %u of its entries %s contained within\n\
  594.   this zipfile, out of a total of %u %s.  The entire central\n\
  595.   directory is %lu (%.8lXh) bytes long, and its offset in bytes from\n\
  596.   the beginning of the zipfile in which it begins is %lu (%.8lXh).\n\n",
  597.               ecrec.number_this_disk,
  598.               ecrec.num_disk_with_start_central_dir,
  599.               ecrec.num_entries_centrl_dir_ths_disk,
  600.               (ecrec.num_entries_centrl_dir_ths_disk == 1)? "is" : "are",
  601.               ecrec.total_entries_central_dir,
  602.               (ecrec.total_entries_central_dir == 1) ? "entry" : "entries",
  603.               ecrec.size_central_directory, ecrec.size_central_directory,
  604.               ecrec.offset_start_central_directory,
  605.               ecrec.offset_start_central_directory);
  606.         }
  607.  
  608.     /*-----------------------------------------------------------------------
  609.         Get the zipfile comment, if any, and print it out.  (Comment may be
  610.         up to 64KB long.  May the fleas of a thousand camels infest the arm-
  611.         pits of anyone who actually takes advantage of this fact.)
  612.       -----------------------------------------------------------------------*/
  613.  
  614.         if (!ecrec.zipfile_comment_length)
  615.             printf("  There is no zipfile comment.\n");
  616.         else {
  617.             printf("  The zipfile comment is %u bytes long and contains the following text:\n\n",
  618.               ecrec.zipfile_comment_length );
  619.             printf("======================== zipfile comment begins ==========================\n");
  620.             if (do_string(ecrec.zipfile_comment_length, DISPLAY))
  621.                 error = PK_WARN;
  622.             printf("\n========================= zipfile comment ends ===========================\n");
  623.             if (error)
  624.                 printf("\n  The zipfile comment is truncated.\n");
  625.         } /* endif (comment exists) */
  626.  
  627.     /* non-verbose mode:  print zipfile comment only if requested */
  628.     } else if (zflag && ecrec.zipfile_comment_length) {
  629.         if (do_string(ecrec.zipfile_comment_length,DISPLAY)) {
  630.             fprintf(stderr, "\ncaution:  zipfile comment truncated\n");
  631.             error = PK_WARN;
  632.         }
  633.     } /* endif (verbose) */
  634.  
  635.     return error;
  636.  
  637. } /* end function process_end_central_dir() */
  638.  
  639.  
  640.  
  641.  
  642.  
  643. /************************************/
  644. /*  Function process_central_dir()  */
  645. /************************************/
  646.  
  647. int process_central_dir()   /* return PK-type error code */
  648. {
  649.     int   do_this_file=FALSE, error, error_in_archive=PK_COOL;
  650.     int   *fn_matched=NULL, *xn_matched=NULL;
  651.     ush   j, members=0;
  652.     ulg   c=0L, uc=0L;
  653.  
  654.  
  655. /*---------------------------------------------------------------------------
  656.     Malloc space for check on unmatched filespecs (no big deal if one or both
  657.     are NULL).
  658.   ---------------------------------------------------------------------------*/
  659.  
  660.     if (filespecs > 0  &&
  661.         (fn_matched=(int *)malloc(filespecs*sizeof(int))) != NULL)
  662.         for (j = 0;  j < filespecs;  ++j)
  663.             fn_matched[j] = FALSE;
  664.  
  665.     if (xfilespecs > 0  &&
  666.         (xn_matched=(int *)malloc(xfilespecs*sizeof(int))) != NULL)
  667.         for (j = 0;  j < xfilespecs;  ++j)
  668.             xn_matched[j] = FALSE;
  669.  
  670. /*---------------------------------------------------------------------------
  671.     Set file pointer to start of central directory, then loop through cen-
  672.     tral directory entries.  Check that directory-entry signature bytes are
  673.     actually there (just a precaution), then process the entry.  We know
  674.     the entire central directory is on this disk:  we wouldn't have any of
  675.     this information unless the end-of-central-directory record was on this
  676.     disk, and we wouldn't have gotten to this routine unless this is also
  677.     the disk on which the central directory starts.  In practice, this had
  678.     better be the *only* disk in the archive, but maybe someday we'll add
  679.     multi-disk support.
  680.   ---------------------------------------------------------------------------*/
  681.  
  682.     pInfo->lcflag = 0;   /* match(), do_string():  never TRUE in zipinfo */
  683.  
  684.     for (j = 0;  j < ecrec.total_entries_central_dir;  ++j) {
  685.         if (readbuf(sig, 4) <= 0)
  686.             return PK_EOF;
  687.         if (strncmp(sig, central_hdr_sig, 4)) {  /* just to make sure */
  688.             fprintf(stderr, CentSigMsg, j);  /* sig not found */
  689.             return PK_BADERR;
  690.         }
  691.         if ((error = get_cdir_file_hdr()) != PK_COOL)
  692.             return error;       /* only PK_EOF defined */
  693.         if ((error = do_string(crec.filename_length, FILENAME)) != PK_COOL) {
  694.           error_in_archive = error;   /* might be warning */
  695.           if (error > PK_WARN)        /* fatal */
  696.               return error;
  697.         }
  698.  
  699.         if (!process_all_files) {    /* check if specified on command line */
  700.             char  **pfn = pfnames-1;
  701.  
  702.             do_this_file = FALSE;
  703.             while (*++pfn)
  704.                 if (match(filename, *pfn)) {
  705.                     do_this_file = TRUE;
  706.                     if (fn_matched)
  707.                         fn_matched[pfn-pfnames] = TRUE;
  708.                     break;       /* found match, so stop looping */
  709.                 }
  710.             if (do_this_file) {  /* check if this is an excluded file */
  711.                 char  **pxn = pxnames-1;
  712.  
  713.                 while (*++pxn)
  714.                     if (match(filename, *pxn)) {
  715.                         do_this_file = FALSE;
  716.                         if (xn_matched)
  717.                             xn_matched[pxn-pxnames] = TRUE;
  718.                         break;
  719.                     }
  720.             }
  721.         }
  722.  
  723.     /*-----------------------------------------------------------------------
  724.         If current file was specified on command line, or if no names were
  725.         specified, do the listing for this file.  Otherwise, get rid of the
  726.         file comment and go back for the next file.
  727.       -----------------------------------------------------------------------*/
  728.  
  729.         if (process_all_files || do_this_file) {
  730.             switch (lflag) {
  731.                 case 1:
  732.                 case 2:
  733.                     printf("%s\n", filename);
  734.                     SKIP_(crec.extra_field_length)
  735.                     SKIP_(crec.file_comment_length)
  736.                     break;
  737.  
  738.                 case 3:
  739.                 case 4:
  740.                 case 5:
  741.                     if ((error = short_info()) != PK_COOL) {
  742.                         error_in_archive = error;   /* might be warning */
  743.                         if (error > PK_WARN)        /* fatal */
  744.                             return error;
  745.                     }
  746.                     break;
  747.  
  748.                 case 10:
  749. #ifdef T20_VMS  /* GRR:  add cbreak-style "more" */
  750.                     printf("\nCentral directory entry #%d:\n", j);
  751. #else
  752.                     /* formfeed/CR for piping to "more": */
  753.                     printf("%s\nCentral directory entry #%d:\n", "\014", j);
  754. #endif
  755.                     printf("---------------------------\n\n");
  756.  
  757.                     if ((error = long_info()) != PK_COOL) {
  758.                       error_in_archive = error;   /* might be warning */
  759.                       if (error > PK_WARN)        /* fatal */
  760.                           return error;
  761.                     }
  762.                     break;
  763.  
  764.                 default:
  765.                     SKIP_(crec.extra_field_length)
  766.                     SKIP_(crec.file_comment_length)
  767.                     break;
  768.  
  769.             } /* end switch (lflag) */
  770.  
  771.             c += crec.csize;
  772.             uc += crec.ucsize;
  773.             if (crec.general_purpose_bit_flag & 1)
  774.                 c -= 12;    /* if encrypted, don't count encryption header */
  775.             ++members;
  776.  
  777.         } else {   /* not listing */
  778.             SKIP_(crec.extra_field_length)
  779.             SKIP_(crec.file_comment_length)
  780.  
  781.         } /* end if (list member?) */
  782.  
  783.     } /* end for-loop (j: member files) */
  784.  
  785. /*---------------------------------------------------------------------------
  786.     Double check that we're back at the end-of-central-directory record.
  787.   ---------------------------------------------------------------------------*/
  788.  
  789.     readbuf(sig, 4);
  790.     if (strncmp(sig, end_central_sig, 4)) {     /* just to make sure again */
  791.         fprintf(stderr, EndSigMsg);  /* didn't find end-of-central-dir sig */
  792.         error_in_archive = PK_WARN;
  793.     }
  794.  
  795. /*---------------------------------------------------------------------------
  796.     Check that we actually found requested files; if so, print totals.
  797.   ---------------------------------------------------------------------------*/
  798.  
  799.     if (tflag)
  800.         printf(
  801.           "%d file%s, %lu bytes uncompressed, %lu bytes compressed:  %d%%\n",
  802.           members, (members==1)? "":"s", uc, c, (uc==0)? 0 : ((uc>2000000L)?
  803.           ((int)((uc-c)/(uc/1000L))+5)/10 : ((int)((1000L*(uc-c))/uc)+5)/10) );
  804.  
  805. /*---------------------------------------------------------------------------
  806.     Check for unmatched filespecs on command line and print warning if any
  807.     found.
  808.   ---------------------------------------------------------------------------*/
  809.  
  810.     if (fn_matched) {
  811.         for (j = 0;  j < filespecs;  ++j)
  812.             if (!fn_matched[j])
  813.                 fprintf(stderr, "caution: filename not matched:  %s\n",
  814.                   pfnames[j]);
  815.         free(fn_matched);
  816.     }
  817.     if (xn_matched) {
  818.         for (j = 0;  j < xfilespecs;  ++j)
  819.             if (!xn_matched[j])
  820.                 fprintf(stderr, "caution: excluded filename not matched:  %s\n",
  821.                   pxnames[j]);
  822.         free(xn_matched);
  823.     }
  824.  
  825.     return error_in_archive;
  826.  
  827. } /* end function process_central_dir() */
  828.  
  829.  
  830.  
  831.  
  832.  
  833. /**************************/
  834. /*  Function long_info()  */
  835. /**************************/
  836.  
  837. int long_info()   /* return PK-type error code */
  838. {
  839.     int          error, error_in_archive=PK_COOL;
  840.     ush          hostver, extver, xattr;
  841.     char         workspace[12], attribs[22];
  842.     static char  unkn[16];
  843.     static char  *os[NUM_HOSTS+1] = {"MS-DOS, OS/2 or NT FAT", "Amiga",
  844.                      "VAX VMS", "Unix", "VM/CMS", "Atari ST", "OS/2 or NT HPFS",
  845.                      "Macintosh", "Z-System", "CP/M", "TOPS-20", "NT NTFS",
  846.                      "unknown" };
  847.     static char  *method[NUM_METHODS+1] = {"none (stored)", "shrunk",
  848.                      "reduced (factor 1)", "reduced (factor 2)",
  849.                      "reduced (factor 3)", "reduced (factor 4)",
  850.                      "imploded", "tokenized", "deflated", unkn};
  851.     static char  *dtype[4] = {"normal", "maximum", "fastest", "undefined"};
  852.  
  853.  
  854. /*---------------------------------------------------------------------------
  855.     Print out various interesting things about the compressed file.
  856.   ---------------------------------------------------------------------------*/
  857.  
  858.     hostnum = MIN(crec.version_made_by[1], NUM_HOSTS);
  859.     hostver = crec.version_made_by[0];
  860.     extnum = MIN(crec.version_needed_to_extract[1], NUM_HOSTS);
  861.     extver = crec.version_needed_to_extract[0];
  862.     methnum = MIN(crec.compression_method, NUM_METHODS);
  863.     if (methnum == NUM_METHODS)
  864.         sprintf(unkn, "unknown (%d)", crec.compression_method);
  865.  
  866.     printf("  %s\n", filename);
  867.  
  868.     printf("\n  host operating system (created on):               %s\n",
  869.       os[hostnum]);
  870.     printf("  version of encoding software:                     %d.%d\n",
  871.       hostver/10, hostver%10);
  872.     printf("  minimum operating system compatibility required:  %s\n",
  873.       os[extnum]);
  874.     printf("  minimum software version required to extract:     %d.%d\n",
  875.       extver/10, extver%10);
  876.     printf("  compression method:                               %s\n",
  877.       method[methnum]);
  878.     if (methnum == IMPLODED) {
  879.         printf("  size of sliding dictionary (implosion):           %cK\n",
  880.           (crec.general_purpose_bit_flag & 2)? '8' : '4');
  881.         printf("  number of Shannon-Fano trees (implosion):         %c\n",
  882.           (crec.general_purpose_bit_flag & 4)? '3' : '2');
  883.     } else if (methnum == DEFLATED) {
  884.         ush  dnum=(crec.general_purpose_bit_flag>>1) & 3;
  885.         printf("  compression sub-type (deflation):                 %s\n",
  886.           dtype[dnum]);
  887.     }
  888.     printf("  file security status:                             %sencrypted\n",
  889.       (crec.general_purpose_bit_flag & 1)? "" : "not ");
  890.     printf("  extended local header:                            %s\n",
  891.       (crec.general_purpose_bit_flag & 8)? "yes" : "no");
  892.     /* print upper 3 bits for amusement? */
  893.     printf("  file last modified on:                            %s\n",
  894.       zipinfo_time(&crec.last_mod_file_date, &crec.last_mod_file_time));
  895.     printf("  32-bit CRC value (hex):                           %.8lx\n",
  896.       crec.crc32);
  897.     printf("  compressed size:                                  %lu bytes\n",
  898.       crec.csize);
  899.     printf("  uncompressed size:                                %lu bytes\n",
  900.       crec.ucsize);
  901.     printf("  length of filename:                               %u characters\n",
  902.       crec.filename_length);
  903.     printf("  length of extra field:                            %u bytes\n",
  904.       crec.extra_field_length);
  905.     printf("  length of file comment:                           %u characters\n",
  906.       crec.file_comment_length);
  907.     printf("  disk number on which file begins:                 disk %u\n",
  908.       crec.disk_number_start);
  909.     printf("  apparent file type:                               %s\n",
  910.       (crec.internal_file_attributes & 1)? "text" : "binary");
  911. /*
  912.     printf("  external file attributes (hex):                   %.8lx\n",
  913.       crec.external_file_attributes);
  914.  */
  915.     xattr = (ush)((crec.external_file_attributes >> 16) & 0xFFFF);
  916.     if (hostnum == VMS_) {
  917.         char   *p=attribs, *q=attribs+1;
  918.         int    i, j, k;
  919.  
  920.         for (k = 0;  k < 12;  ++k)
  921.             workspace[k] = 0;
  922.         if (xattr & S_IRUSR)
  923.             workspace[0] = 'R';
  924.         if (xattr & S_IWUSR) {
  925.             workspace[1] = 'W';
  926.             workspace[3] = 'D';
  927.         }
  928.         if (xattr & S_IXUSR)
  929.             workspace[2] = 'E';
  930.         if (xattr & S_IRGRP)
  931.             workspace[4] = 'R';
  932.         if (xattr & S_IWGRP) {
  933.             workspace[5] = 'W';
  934.             workspace[7] = 'D';
  935.         }
  936.         if (xattr & S_IXGRP)
  937.             workspace[6] = 'E';
  938.         if (xattr & S_IROTH)
  939.             workspace[8] = 'R';
  940.         if (xattr & S_IWOTH) {
  941.             workspace[9] = 'W';
  942.             workspace[11] = 'D';
  943.         }
  944.         if (xattr & S_IXOTH)
  945.             workspace[10] = 'E';
  946.  
  947.         *p++ = '(';
  948.         for (k = j = 0;  j < 3;  ++j) {    /* loop over groups of permissions */
  949.             for (i = 0;  i < 4;  ++i, ++k)  /* loop over perms within a group */
  950.                 if (workspace[k])
  951.                     *p++ = workspace[k];
  952.             *p++ = ',';                       /* group separator */
  953.             if (j == 0)
  954.                 while ((*p++ = *q++) != ','); /* system, owner perms are same */
  955.         }
  956.         *p-- = 0;
  957.         *p = ')';   /* overwrite last comma */
  958.         printf("  VMS file attributes (%06o octal):               %s\n",
  959.           xattr, attribs);
  960.  
  961.     } else if (hostnum == AMIGA_) {
  962.         switch (xattr & AMI_IFMT) {
  963.             case AMI_IFDIR:  attribs[0] = 'd';  break;
  964.             case AMI_IFREG:  attribs[0] = '-';  break;
  965.             default:         attribs[0] = '?';  break;
  966.         }
  967.         attribs[1] = (xattr & AMI_IHIDDEN)?   'h' : '-';
  968.         attribs[2] = (xattr & AMI_ISCRIPT)?   's' : '-';
  969.         attribs[3] = (xattr & AMI_IPURE)?     'p' : '-';
  970.         attribs[4] = (xattr & AMI_IARCHIVE)?  'a' : '-';
  971.         attribs[5] = (xattr & AMI_IREAD)?     'r' : '-';
  972.         attribs[6] = (xattr & AMI_IWRITE)?    'w' : '-';
  973.         attribs[7] = (xattr & AMI_IEXECUTE)?  'e' : '-';
  974.         attribs[8] = (xattr & AMI_IDELETE)?   'd' : '-';
  975.         attribs[9] = 0;   /* better dlm the string */
  976.         printf("  Amiga file attributes (%06o octal):             %s\n",
  977.           xattr, attribs);
  978.  
  979.     } else if ((hostnum != FS_FAT_) && (hostnum != FS_HPFS_) &&
  980.         (hostnum != FS_NTFS_)) {
  981.         /* assume Unix-like */
  982.         switch (xattr & UNX_IFMT) {
  983.             case UNX_IFDIR:   attribs[0] = 'd';  break;
  984.             case UNX_IFREG:   attribs[0] = '-';  break;
  985.             case UNX_IFLNK:   attribs[0] = 'l';  break;
  986.             case UNX_IFBLK:   attribs[0] = 'b';  break;
  987.             case UNX_IFCHR:   attribs[0] = 'c';  break;
  988.             case UNX_IFIFO:   attribs[0] = 'p';  break;
  989.             case UNX_IFSOCK:  attribs[0] = 's';  break;
  990.             default:          attribs[0] = '?';  break;
  991.         }
  992.         attribs[1] = (xattr & S_IRUSR)? 'r' : '-';
  993.         attribs[4] = (xattr & S_IRGRP)? 'r' : '-';
  994.         attribs[7] = (xattr & S_IROTH)? 'r' : '-';
  995.  
  996.         attribs[2] = (xattr & S_IWUSR)? 'w' : '-';
  997.         attribs[5] = (xattr & S_IWGRP)? 'w' : '-';
  998.         attribs[8] = (xattr & S_IWOTH)? 'w' : '-';
  999.  
  1000.         if (xattr & S_IXUSR)
  1001.             attribs[3] = (xattr & UNX_ISUID)? 's' : 'x';
  1002.         else
  1003.             attribs[3] = (xattr & UNX_ISUID)? 'S' : '-';   /* S = undefined */
  1004.         if (xattr & S_IXGRP)
  1005.             attribs[6] = (xattr & UNX_ISGID)? 's' : 'x';   /* == UNX_ENFMT */
  1006.         else
  1007.             attribs[6] = (xattr & UNX_ISGID)? 'l' : '-';
  1008.         if (xattr & S_IXOTH)
  1009.             attribs[9] = (xattr & UNX_ISVTX)? 't' : 'x';   /* "sticky bit" */
  1010.         else
  1011.             attribs[9] = (xattr & UNX_ISVTX)? 'T' : '-';   /* T = undefined */
  1012.         attribs[10] = 0;
  1013.  
  1014.         printf("  Unix file attributes (%06o octal):              %s\n",
  1015.           xattr, attribs);
  1016.  
  1017.     } /* endif (hostnum: external attributes format) */
  1018.  
  1019.     if ((xattr=(ush)(crec.external_file_attributes & 0xFF)) == 0)
  1020.         printf("  MS-DOS file attributes (%02X hex):                  none\n",
  1021.           xattr);
  1022.     else if (xattr == 1)
  1023.         printf(
  1024.           "  MS-DOS file attributes (%02X hex):                  read-only\n",
  1025.           xattr);
  1026.     else
  1027.         printf(
  1028.          "  MS-DOS file attributes (%02X hex):                  %s%s%s%s%s%s\n",
  1029.           xattr, (xattr&1)?"rdo ":"", (xattr&2)?"hid ":"", (xattr&4)?"sys ":"",
  1030.           (xattr&8)?"lab ":"", (xattr&16)?"dir ":"", (xattr&32)?"arc":"");
  1031.     printf(
  1032.      "  offset of local header from start of archive:     %lu (%.8lXh) bytes\n",
  1033.       crec.relative_offset_local_header, crec.relative_offset_local_header);
  1034.  
  1035. /*---------------------------------------------------------------------------
  1036.     Skip the extra field, if any, and print the file comment, if any (the
  1037.     filename has already been printed, above).  That finishes up this file
  1038.     entry...
  1039.   ---------------------------------------------------------------------------*/
  1040.  
  1041.     if (crec.extra_field_length > 0) {
  1042. /* #ifdef OS2 */
  1043. #if TRUE
  1044.         ulg ea_size;
  1045.         if ((error = do_string(crec.extra_field_length, EXTRA_FIELD)) != 0) {
  1046.             error_in_archive = error;
  1047.             if (error > PK_WARN)   /* fatal:  can't continue */
  1048.                 return error;
  1049.         }
  1050.         if ((ea_size = SizeOfEAs(extra_field)) != 0)
  1051.             printf("\n\
  1052.   This file has %lu bytes of OS/2 EA's in the local extra field.\n\
  1053.   (May not match OS/2 \"dir\" amount due to storage method.)\n\n",
  1054.               ea_size);
  1055.         else
  1056.             printf("\n  There is an unknown extra field (skipping).\n");
  1057. #else
  1058.         printf("\n  There is an extra field (skipping).\n");
  1059.         SKIP_(crec.extra_field_length)
  1060. #endif
  1061.     } else
  1062.         printf("\n");
  1063.  
  1064.     if (!crec.file_comment_length)
  1065.         printf("  There is no file comment.\n");
  1066.     else {
  1067.         printf("\
  1068. ------------------------- file comment begins ----------------------------\n");
  1069.         if ((error = do_string(crec.file_comment_length, DISPLAY)) != PK_COOL) {
  1070.           error_in_archive = error;   /* might be warning */
  1071.           if (error > PK_WARN)   /* fatal */
  1072.               return error;
  1073.         }
  1074.         printf("\n\
  1075. -------------------------- file comment ends -----------------------------\n");
  1076.     }
  1077.  
  1078.     return error_in_archive;
  1079.  
  1080. } /* end function long_info() */
  1081.  
  1082.  
  1083.  
  1084.  
  1085.  
  1086. /**************************/
  1087. /*  Function SizeOfEAs()  */
  1088. /**************************/
  1089.  
  1090. ulg SizeOfEAs(extra_field)   /* Author: Kai Uwe Rommel */
  1091.     void   *extra_field;
  1092. {
  1093.     EAHEADER *pEAblock = (PEAHEADER)extra_field;
  1094.  
  1095.     if (extra_field != NULL  &&  pEAblock->nID == EAID)
  1096.         return pEAblock->lSize;
  1097.  
  1098.     return 0L;
  1099. }
  1100.  
  1101.  
  1102.  
  1103.  
  1104.  
  1105. /***************************/
  1106. /*  Function short_info()  */
  1107. /***************************/
  1108.  
  1109. int short_info()   /* return PK-type error code */
  1110. {
  1111.     int           k, error, error_in_archive=PK_COOL;
  1112.     ush           hostver, xattr;
  1113.     char          workspace[12], attribs[16];
  1114.     static char   impl[5]="i#:#", defl[5]="def#", unkn[8];
  1115.     static char   dtype[5]="NXF?";  /* normal, maximum, fastest, undefined */
  1116.     static char   *os[NUM_HOSTS+1] = {"fat", "ami", "vms", "unx", "cms",
  1117.                       "atr", "hpf", "mac", "zzz", "cpm", "t20", "ntf", "???" };
  1118.     static char   *method[NUM_METHODS+1] = {"stor", "shrk", "re:1", "re:2",
  1119.                       "re:3", "re:4", impl, "tokn", defl, unkn};
  1120.  
  1121.  
  1122. /*---------------------------------------------------------------------------
  1123.     Print out various interesting things about the compressed file.
  1124.   ---------------------------------------------------------------------------*/
  1125.  
  1126.     methnum = MIN(crec.compression_method, NUM_METHODS);
  1127.     hostnum = MIN(crec.version_made_by[1], NUM_HOSTS);
  1128.     hostver = crec.version_made_by[0];
  1129. /*
  1130.     extnum = MIN(crec.version_needed_to_extract[1], NUM_HOSTS);
  1131.     extver = crec.version_needed_to_extract[0];
  1132.  */
  1133.  
  1134.     if (methnum == IMPLODED) {
  1135.         impl[1] = (char) ((crec.general_purpose_bit_flag & 2)? '8' : '4');
  1136.         impl[3] = (char) ((crec.general_purpose_bit_flag & 4)? '3' : '2');
  1137.     } else if (methnum == DEFLATED) {
  1138.         ush  dnum=(crec.general_purpose_bit_flag>>1) & 3;
  1139.         defl[3] = dtype[dnum];
  1140.     } else if (methnum == NUM_METHODS) {   /* unknown */
  1141.         sprintf(unkn, "u%03d", crec.compression_method);
  1142.     }
  1143.  
  1144.     for (k = 0;  k < 15;  ++k)
  1145.         attribs[k] = ' ';
  1146.     attribs[15] = 0;
  1147.  
  1148.     xattr = (ush)((crec.external_file_attributes >> 16) & 0xFFFF);
  1149.     switch (hostnum) {
  1150.         case VMS_:
  1151.             {   char   *p=attribs;
  1152.                 int    i, j;
  1153.  
  1154.                 for (k = 0;  k < 12;  ++k)
  1155.                     workspace[k] = 0;
  1156.                 if (xattr & S_IRUSR)
  1157.                     workspace[0] = 'R';
  1158.                 if (xattr & S_IWUSR) {
  1159.                     workspace[1] = 'W';
  1160.                     workspace[3] = 'D';
  1161.                 }
  1162.                 if (xattr & S_IXUSR)
  1163.                     workspace[2] = 'E';
  1164.                 if (xattr & S_IRGRP)
  1165.                     workspace[4] = 'R';
  1166.                 if (xattr & S_IWGRP) {
  1167.                     workspace[5] = 'W';
  1168.                     workspace[7] = 'D';
  1169.                 }
  1170.                 if (xattr & S_IXGRP)
  1171.                   workspace[6] = 'E';
  1172.                 if (xattr & S_IROTH)
  1173.                     workspace[8] = 'R';
  1174.                 if (xattr & S_IWOTH) {
  1175.                     workspace[9] = 'W';
  1176.                     workspace[11] = 'D';
  1177.                 }
  1178.                 if (xattr & S_IXOTH)
  1179.                     workspace[10] = 'E';
  1180.  
  1181.                 for (k = j = 0;  j < 3;  ++j) {     /* groups of permissions */
  1182.                     for (i = 0;  i < 4;  ++i, ++k)  /* perms within a group */
  1183.                         if (workspace[k])
  1184.                             *p++ = workspace[k];
  1185.                     *p++ = ',';                     /* group separator */
  1186.                 }
  1187.                 *--p = ' ';   /* overwrite last comma */
  1188.                 if ((p - attribs) < 12)
  1189.                     sprintf(&attribs[12], "%d.%d", hostver/10, hostver%10);
  1190.             }
  1191.             break;
  1192.  
  1193.         case FS_FAT_:
  1194.         case FS_HPFS_:
  1195.         case FS_NTFS_:
  1196.             xattr = (ush)(crec.external_file_attributes & 0xFF);
  1197.             sprintf(attribs, "%s,%s,%s,%s", (xattr&32)?"arc":"",
  1198.               (xattr&2)?"hid":"", (xattr&1)?"rdo":"rw", (xattr&4)?"sys":"");
  1199.             if ((k = strlen(attribs)) < 15)
  1200.                 attribs[k] = ' ';   /* overwrite '\0' */
  1201.             if (k < 12)
  1202.                 sprintf(&attribs[12], "%d.%d", hostver/10, hostver%10);
  1203.             break;
  1204.  
  1205.         case AMIGA_:
  1206.             switch (xattr & AMI_IFMT) {
  1207.                 case AMI_IFDIR:  attribs[0] = 'd';  break;
  1208.                 case AMI_IFREG:  attribs[0] = '-';  break;
  1209.                 default:         attribs[0] = '?';  break;
  1210.             }
  1211.             attribs[1] = (xattr & AMI_IHIDDEN)?   'h' : '-';
  1212.             attribs[2] = (xattr & AMI_ISCRIPT)?   's' : '-';
  1213.             attribs[3] = (xattr & AMI_IPURE)?     'p' : '-';
  1214.             attribs[4] = (xattr & AMI_IARCHIVE)?  'a' : '-';
  1215.             attribs[5] = (xattr & AMI_IREAD)?     'r' : '-';
  1216.             attribs[6] = (xattr & AMI_IWRITE)?    'w' : '-';
  1217.             attribs[7] = (xattr & AMI_IEXECUTE)?  'e' : '-';
  1218.             attribs[8] = (xattr & AMI_IDELETE)?   'd' : '-';
  1219.             sprintf(&attribs[12], "%d.%d", hostver/10, hostver%10);
  1220.             break;
  1221.  
  1222.         default:   /* assume Unix-like */
  1223.             switch (xattr & UNX_IFMT) {
  1224.                 case UNX_IFDIR:   attribs[0] = 'd';  break;
  1225.                 case UNX_IFREG:   attribs[0] = '-';  break;
  1226.                 case UNX_IFLNK:   attribs[0] = 'l';  break;
  1227.                 case UNX_IFBLK:   attribs[0] = 'b';  break;
  1228.                 case UNX_IFCHR:   attribs[0] = 'c';  break;
  1229.                 case UNX_IFIFO:   attribs[0] = 'p';  break;
  1230.                 case UNX_IFSOCK:  attribs[0] = 's';  break;
  1231.                 default:          attribs[0] = '?';  break;
  1232.             }
  1233.             attribs[1] = (xattr & S_IRUSR)? 'r' : '-';
  1234.             attribs[4] = (xattr & S_IRGRP)? 'r' : '-';
  1235.             attribs[7] = (xattr & S_IROTH)? 'r' : '-';
  1236.             attribs[2] = (xattr & S_IWUSR)? 'w' : '-';
  1237.             attribs[5] = (xattr & S_IWGRP)? 'w' : '-';
  1238.             attribs[8] = (xattr & S_IWOTH)? 'w' : '-';
  1239.  
  1240.             if (xattr & S_IXUSR)
  1241.                 attribs[3] = (xattr & UNX_ISUID)? 's' : 'x';
  1242.             else
  1243.                 attribs[3] = (xattr & UNX_ISUID)? 'S' : '-';  /* S==undefined */
  1244.             if (xattr & S_IXGRP)
  1245.                 attribs[6] = (xattr & UNX_ISGID)? 's' : 'x';  /* == UNX_ENFMT */
  1246.             else
  1247.                 attribs[6] = (xattr & UNX_ISGID)? 'l' : '-';
  1248.             if (xattr & S_IXOTH)
  1249.                 attribs[9] = (xattr & UNX_ISVTX)? 't' : 'x';  /* "sticky bit" */
  1250.             else
  1251.                 attribs[9] = (xattr & UNX_ISVTX)? 'T' : '-';  /* T==undefined */
  1252.  
  1253.             sprintf(&attribs[12], "%d.%d", hostver/10, hostver%10);
  1254.             break;
  1255.  
  1256.     } /* end switch (hostnum: external attributes format) */
  1257.  
  1258.     printf("%s %s %7lu %c%c", attribs, os[hostnum], crec.ucsize,
  1259.       (crec.general_purpose_bit_flag & 1)?
  1260.       ((crec.internal_file_attributes & 1)? 'T' : 'B') :   /* encrypted */
  1261.       ((crec.internal_file_attributes & 1)? 't' : 'b'),    /* plaintext */
  1262.       (crec.general_purpose_bit_flag & 8)? (crec.extra_field_length? 'X' : 'l')
  1263.                                         : (crec.extra_field_length? 'x' : '-'));
  1264.     if (lflag == 4) {
  1265.         longint c = (longint)crec.csize;
  1266.         longint uc = (longint)crec.ucsize;
  1267.  
  1268.         if (crec.general_purpose_bit_flag & 1)
  1269.             c -= 12;    /* if encrypted, don't count encryption header */
  1270.         /* risk signed overflow if blindly multiply: */
  1271.         printf("%3d%%", (uc==0)? 0 : ((uc>2000000L)?
  1272.           ((int)((uc-c)/(uc/1000L))+5)/10 : ((int)((1000L*(uc-c))/uc)+5)/10) );
  1273.     } else if (lflag == 5)
  1274.         printf(" %7lu", crec.csize);
  1275.  
  1276.     printf(" %s %s %s\n", method[methnum],
  1277.       zipinfo_time(&crec.last_mod_file_date, &crec.last_mod_file_time),
  1278.       filename);
  1279.  
  1280. /*---------------------------------------------------------------------------
  1281.     Skip the extra field and/or the file comment, if any (the filename has
  1282.     already been printed, above).  That finishes up this file entry...
  1283.   ---------------------------------------------------------------------------*/
  1284.  
  1285.     SKIP_(crec.extra_field_length)
  1286.     SKIP_(crec.file_comment_length)
  1287.  
  1288.     return error_in_archive;
  1289.  
  1290. } /* end function short_info() */
  1291.  
  1292.  
  1293.  
  1294.  
  1295.  
  1296. /*****************************/
  1297. /*  Function zipinfo_time()  */
  1298. /*****************************/
  1299.  
  1300. char *zipinfo_time(datez, timez)
  1301.     ush   *datez, *timez;
  1302. {
  1303.     ush           yr, mo, dy, hh, mm, ss;
  1304.     static char   d_t_str[21];
  1305.     static char   *month[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  1306.                                 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  1307.  
  1308.  
  1309.  
  1310. /*---------------------------------------------------------------------------
  1311.     Convert the file-modification date and time info to a string of the form
  1312.     "23 Feb 1990 17:15:00" or "23-Feb-91 17:15," depending on value of lflag.
  1313.   ---------------------------------------------------------------------------*/
  1314.  
  1315.     yr = ((*datez >> 9) & 0x7f) + 80;
  1316.     mo = ((*datez >> 5) & 0x0f) - 1;
  1317.     dy = *datez & 0x1f;
  1318.  
  1319.     hh = (*timez >> 11) & 0x1f;
  1320.     mm = (*timez >> 5) & 0x3f;
  1321.     ss = (*timez & 0x1f) * 2;
  1322.  
  1323.     if ((lflag >= 3) && (lflag <= 5))
  1324.         sprintf(d_t_str, "%2u-%s-%u %02u:%02u", dy, month[mo], yr, hh, mm);
  1325.     else if (lflag > 9)  /* verbose listing format */
  1326.         sprintf(d_t_str, "%u %s %u %02u:%02u:%02u", dy, month[mo], yr+1900,
  1327.           hh, mm, ss);
  1328.  
  1329.     return d_t_str;
  1330.  
  1331. } /* end function zipinfo_time() */
  1332.